﻿using System;
using System.Threading;
using System.Threading.Tasks;

namespace eslib.nnp5.Tools
{
    /// <summary>
    /// 等待action
    /// 一个对象对应一个网络操作
    /// </summary>
    /// <example>
    /// WaitAction action = new WaitAction(3000);
    /// async Task<(bool success, object result)> add(int a, int b)
    /// {
    ///     return await action.Run(() => { });     //这里发送数据包
    /// }
    /// 
    /// //网络响应时设置结果(在数据包接收事件中)
    /// action.Set(null);       //set的值将作为result在Run返回
    /// </example>
    public class WaitAction
    {
        /// <summary>
        /// 超时时间
        /// </summary>
        public int TimeoutMs { get; set; }

        AutoResetEvent waitEvt = null;


        /// <summary>
        /// 绑定对象，表示结果，每次Run会被置null.
        /// 由Set()设置
        /// </summary>
        public object Result
        {
            get { lock (this) { return result; } }
            protected set { lock (this) { result = value; } }
        }
        object result;


        /// <summary>
        /// Set()被调用后置为true
        /// </summary>
        bool SetCalled
        {
            get
            {
                lock (this) { return setCalled; }
            }
            set
            {
                lock (this) { setCalled = value; }
            }
        }
        bool setCalled = false;



        /// <summary>
        /// 构造，指定超时时间
        /// </summary>
        /// <param name="timeoutMs"></param>
        public WaitAction(int timeoutMs)
        {
            this.TimeoutMs = timeoutMs;
        }


        /// <summary>
        /// 构造，无限等待
        /// </summary>
        public WaitAction()
        {
            TimeoutMs = -1;     //无限等待
        }





        /// <summary>
        /// 异步执行一个任务,可等待.返回true表示正确执行完成，返回false表示超时结果
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        public async Task<(bool success, object result)> Run(Action action)
        {
            waitEvt = new AutoResetEvent(false);
            SetCalled = false;
            Result = null;

            return (await Task.Run(() =>
            {
                action();
                waitEvt.WaitOne(TimeoutMs);
                waitEvt.Dispose();        //释放，每次都new
                waitEvt = null;       //置null

                //如果SetCalled，表示正常结束
                return SetCalled;
            }), Result);
        }


        /// <summary>
        /// 触发完成事件
        /// </summary>
        /// <param name="result">填充结果</param>
        public void Set(object result)
        {
            if (waitEvt == null) return;

            try
            {
                waitEvt.Set();
                SetCalled = true;
                this.Result = result;
            }
            catch { }
        }

    }

}
